#include <getopt.h>
#include <stdio.h>
#include <xtables.h>
#include <stdlib.h>
#include <string.h>
#include "../xt_wx.h"

enum {
    O_WX_TYPE =  0,
    O_WX_CODE};

static void wx_help(void)
{
    printf("\ntest wx options:\n"
            "\t--optype optype --opcode opcode\n"
            "\toptype 1: WX_STR_T; 2:WX_CODE_T\n"
            "\topcode: when optype=1, opcode is string, or u_int32_t\n");
}
#if 0
static const struct option wx_opts[] = {
    {.name = "optype", .has_arg = true, .val = '1'},
    {.name = "opcode", .has_arg = true, .val = '2'},
    {NULL},
};
#endif
static const struct xt_option_entry wx_opts[] = {
    {.name = "optype", .id = O_WX_TYPE, .type = XTTYPE_STRING, .flags = XTOPT_MAND},
    {.name = "opcode", .id = O_WX_CODE, .type = XTTYPE_STRING, .flags = XTOPT_MAND},
    XTOPT_TABLEEND,
};
static bool set_u32_info(const char *arg, struct xt_wx_uint32_info *info)
{
    u_int32_t	operation_code = 0;
    operation_code = strtoul(arg, NULL, 16);

    if (operation_code)
        info->operation_code = operation_code;
    else
    {		
        fprintf(stderr, "op_code error [%04x]!\n",operation_code);
        return false;
    }

#ifdef WX_DEBUG
    printf("op_code:%04x\n", info->operation_code);
#endif
    return true;
}

static bool
set_string_info(const char *arg, struct xt_wx_string_info *info)
{
    memcpy(info->operation_code ,arg, strlen(arg));
#ifdef WX_DEBUG
		fprintf(stderr, "op_str:%s\n", info->operation_code);
#endif
	return true;
}


static void wx_parse(struct xt_option_call *cb)
{
    struct xt_wx_info *info = cb->data;
    xtables_option_parse(cb);
    switch (cb->entry->id) {
        case O_WX_TYPE:
            {
            u_int8_t type = strtoul(cb->arg, NULL, 10);
            if ( WX_STR_T == type )
                info->wx_op_type = WX_STR_T;	 
            else if (WX_CODE_T == type)
                info->wx_op_type = WX_CODE_T;
            else
            {
                fprintf(stderr, "wrong im name\n");
                goto failed;
            }
            }
            break;
        case O_WX_CODE:
            switch (info->wx_op_type) {
                case WX_STR_T:
                    if (!set_string_info(cb->arg, &info->op_str)) goto failed;
                    break;
                case WX_CODE_T:
                    if (!set_u32_info(cb->arg, &info->op_code)) goto failed;
                    break;
                default:
                    fprintf(stderr, "no im name is chosen!\n");
                    goto failed;
                    return;
            }
            break;
        default:
            fprintf(stderr, "wrong opt!\n");
    }
    return;

failed:
    memset(info, 0, sizeof(struct xt_wx_info));
    return;
}


static void wx_print(const void *ip, const struct xt_entry_match *match, int numeric)
{
    const struct xt_wx_info *info = (const void *)match->data;
    switch (info->wx_op_type)
    {
        case WX_STR_T:
            printf(" --optype 1 --opcode %s\n", info->op_str.operation_code);

            break;
        case WX_CODE_T:
            printf(" --optype 2 --opcode %u\n",info->op_code.operation_code);
            break;
        default:
            printf("internal wrong!\n");
            return;
    }
}

static void wx_save(const void *ip, const struct xt_entry_match  * match)
{
    const struct xt_wx_info *info = (const void *)match->data;
    switch (info->wx_op_type)
    {
        case WX_STR_T:
            printf(" --optype 1 --opcode %s\n", info->op_str.operation_code);

            break;
        case WX_CODE_T:
            printf(" --optype 2 --opcode %u\n",info->op_code.operation_code);
            break;
        default:
            printf("internal wrong!\n");
            return;
    }
}

static struct xtables_match wx_mt_reg = {
    .family        = NFPROTO_UNSPEC,
    .name          = "wx",
    .version       = XTABLES_VERSION,
    .size          = XT_ALIGN(sizeof(struct xt_wx_info)),
    .userspacesize = XT_ALIGN(sizeof(struct xt_wx_info)),
    .help          = wx_help,
    .print         = wx_print,
    .save          = wx_save,
    .x6_parse      = wx_parse,
    .x6_options    = wx_opts,
};

void _init(void)
{
    xtables_register_match(&wx_mt_reg);
}
